package com.tomtop.core.exception;

import java.io.IOException;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.util.MimeType;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

import com.tomtop._const.Const;
import com.tomtop.config.WebSiteConfig;
import com.tomtop.core.Context;
import com.tomtop.core.model.ResponseEntity;
import com.tomtop.core.utils.ListUtils;
import com.tomtop.core.utils.MapUtils;
import com.tomtop.core.utils.StringUtils;

/**
 * 全局API异常
 *
 */
@ControllerAdvice
public class GlobalApiExceptionHandler extends ResponseEntityExceptionHandler {

	private final static Logger logger = LoggerFactory.getLogger(GlobalApiExceptionHandler.class);

	static AbstractJackson2HttpMessageConverter jsonConverter;

	@Autowired
	void set(AbstractJackson2HttpMessageConverter jsonConverter) {
		GlobalApiExceptionHandler.jsonConverter = jsonConverter;
	}

	@ExceptionHandler(GlobalException.class)
	ModelAndView handleControllerException(HttpServletRequest request, HttpServletResponse response,
			GlobalException ex) {
		return GlobalApiExceptionHandler.outException(request, response, ex);
	}

	@ExceptionHandler(Throwable.class)
	ModelAndView ControllerException(HttpServletRequest request, HttpServletResponse response, Throwable ex) {
		String errorMsg = (ex == null || StringUtils.isEmpty(ex.getMessage())) ? "so sorry,error occured."
				: ex.getMessage();
		GlobalException customException = new GlobalException(GlobalExceptionStatus.FAIL, ex, errorMsg);
		return GlobalApiExceptionHandler.outException(request, response, customException);
	}

	public static ModelAndView outException(HttpServletRequest request, HttpServletResponse response,
			GlobalException ex) {
		GlobalApiExceptionHandler.logLevelOut(ex);
		if (GlobalApiExceptionHandler.isJson(request)) {
			return GlobalApiExceptionHandler.jsonView(ResponseEntity.now(ex), response);
		}
		return controllerFromRequestException(request, response, ex);
	}

	private static void logLevelOut(GlobalException ex) {
		ListUtils<Object> lu = ListUtils.n().a(ex.getMessage()).a(Context.getContext());
		String format = "系统出现了异常{} {}";
		switch (ex.getLevel()) {
		case OFF:
			return;
		case ERROR:
			logger.error(format, lu.a(ex).to().toArray());
			break;
		case WARN:
			logger.warn(format, lu.to().toArray());
			break;
		case INFO:
			logger.info(format, lu.to().toArray());
			break;
		case DEBUG:
			logger.info(format, lu.to().toArray());
			break;
		case TRACE:
			logger.trace(format, lu.to().toArray());
			break;
		default:
			break;
		}
	}

	/**
	 * 判断是否需要响应json
	 * 
	 * @param request
	 * @return
	 */
	private static boolean isJson(HttpServletRequest request) {
		if (Context.isRest())
			return true;
		String contenttype = request.getContentType();
		return !StringUtils.isEmpty(contenttype)
				&& "json".equals(MimeType.valueOf(contenttype).getSubtype().toLowerCase());
	}

	/**
	 * 是否是未登陆异常
	 * 
	 * @return
	 */
	private static ModelAndView isNotLogin(GlobalException e) {
		if (e.getCode() == GlobalExceptionStatus.USER_NOT_LOGIN.getCode()) {
			String backUrl = Context.getRequest().getRequestURL().append("?")
					.append(Context.getRequest().getQueryString()).toString();
			if (logger.isDebugEnabled())
				logger.debug(backUrl);
			String redirectUrl = WebSiteConfig.getLoginBreak(backUrl);
			if (StringUtils.isNotEmpty(redirectUrl))
				return new ModelAndView(Const.REDIRECT_PREFIX + redirectUrl);
		}
		return null;
	}

	/**
	 * 获取异常页面
	 * 
	 * @param e
	 * @return
	 */
	private static String getErrPage(GlobalException e) {
		String url = WebSiteConfig.getParam("ERR_PAGE", "/error");
		if (e != null && e.getGoUrl() != null)
			url = e.getGoUrl();
		if (!url.startsWith(Const.REDIRECT_PREFIX)) {
			String page_prefix;
			if (Context.isPc()) {
				page_prefix = WebSiteConfig.getParam("PC_PAGE_PREFIX", "/pc");
			} else {
				page_prefix = WebSiteConfig.getParam("M_PAGE_PREFIX", "/m");
			}
			url = page_prefix + url;
		}
		return url;
	}

	private static Map<String, Object> getModelMap(GlobalException e) {
		String errorMsg = "so sorry,error occured";
		int errorCode = 500;
		if (e != null) {
			if (StringUtils.isNotEmpty(e.getDetails()))
				errorMsg = e.getDetails();
			else if (StringUtils.isNotEmpty(e.getDescribe()))
				errorMsg = e.getDescribe();
			errorCode = e.getCode();
		}

		String carthome = WebSiteConfig.getCartHome();// 触发异常去到首页
		String shophome = WebSiteConfig.getShopHome();

		return MapUtils.<String, Object>n()
				.a("carthome", carthome)
				.a("shophome", shophome)
				.a("errorCode", errorCode)
				.a("errorMsg", errorMsg)
				.a("website", WebSiteConfig.getWebSite())
				.to();
	}

	/**
	 * 响应异常页面
	 * 
	 * @param request
	 * @param response
	 * @param e
	 * @return
	 */
	private static ModelAndView controllerFromRequestException(HttpServletRequest request, HttpServletResponse response,
			GlobalException e) {
		ModelAndView modelAndView = isNotLogin(e);
		if (modelAndView != null)
			return modelAndView;
		String url = getErrPage(e);
		if (StringUtils.isNotEmpty(url) && url.startsWith(Const.REDIRECT_PREFIX))
			return new ModelAndView(url);
		modelAndView = new ModelAndView(url);
		modelAndView.addAllObjects(getModelMap(e));
		return modelAndView;
	}

	/**
	 * 响应json
	 * 
	 * @param model
	 * @param response
	 * @return
	 */
	private static ModelAndView jsonView(Object model, HttpServletResponse response) {
		try {
			jsonConverter.write(model, MediaType.APPLICATION_JSON, new ServletServerHttpResponse(response));
		} catch (HttpMessageNotWritableException | IOException e) {
			e.printStackTrace();
		}
		return null;
	}
}
